//
// Created by ZYZ on 2020/6/17.
//
#include "stdio.h"
#include <stdlib.h>
#include <iostream>
#include "linear_stack.h"
#include "../../include/type_linear_stack.h"
#include "stack_use5_expression.h"
#ifdef SELEMTYPE_CHAR

//参考 : https://blog.csdn.net/qq_43779149/article/details/104190993?utm_medium=distribute.pc_relevant.none-task-blog-baidujs-8

char OP[] = {'+', '-', '*', '/', '(', ')', '#'};

OperandType GetTop(SqStack S) {
    SElemType e;
    GetTop(S, e);
    return e;
}

//判断字符串 c 师傅在字符数组 op 中
//BOOL In(char c, char op[]) {  //书中是有两个参数，但是就得遍历 op 数组从而判断 c 是否在 op 里了?这里就改成一个参数了
BOOL In(char c){
    switch(c){
        case '+':
        case '-':
        case '*':
        case '/':
        case '(':
        case ')':
        case '#':
            return TRUE;
        default:
            return FALSE;
    }
}

//Precede : [prɪˈsiːd] 超前;先于    //数据结构 p53 页表格
//判断运算符 e1 和 e2 的优先关系
OperatorType Precede(OperatorType e1, OperatorType e2) {

    OperatorType f;
    //从表格的列的左到右依次 case
    switch (e2) {

        case '+':
        case '-':
            if (e1 == '(' || e1 == '#')
                f = '<';
            else f = '>';
            break;

        case '*':
        case '/':
            if (e1 == '*' || e1 == '/' || e1 == ')')
                f = '>';
            else f = '<';
            break;

        case '(':
            if (e1 == ')') {
                printf("括号匹配错误\n");
                exit(-1);
            } else f = '<';
            break;

        case ')':
            switch (e1) {
                case '(':
                    f = '=';
                    break;

                case '#':
                    printf("输出了错误的括号\n");
                    exit(-1);
                    break;

                default:
                    f = '>';
                    break;
            }
            break; //刚开始这里忘记了break

        case '#':
            switch (e1) {
                case '#':
                    f = '=';
                    break;

                case '(':
                    printf("括号匹配错误\n");
                    exit(-1);
                    break;

                default:
                    f = '>';
                    break;
            }
            break; //case中的break只跳出小switch，在这种情况下会继续向大switch下比较
    }
    return f;
}


//使用操作符 c 来对两个操作数 a 和 b 进行运算，注意:返回的是一个字符 n (数值对应的 ascii 码)，所以还需要 n - 48 才能得到真实数值
OperandType Operate(OperandType a, OperatorType c, OperandType b) {
    int x, y;  //0 的 ascii 码是48

    //这是错误的写法 如果是数字0-9那么没有问题 但是这里是可能14 20就不能这样了
    //x=a-'0';
    //y=b-'0';

    x = a - 48;
    y = b - 48;
    //直接用数字是没有问题的

    switch (c) {
        case '+':
            return x + y + 48;
        case '-':
            return x - y + 48;
        case '*':
            return x * y + 48;
        case '/':
            return x / y + 48;
    }
}


//计算表达式的值
//每次输入都会比较运算符栈 OPIR 顶部元素 和 输入的运算符的优先级，如果运算符栈 OPIR 顶部元素是优先级高的(例如乘除法)，则先进行乘除法运算，该运算符栈 OPIR 出栈 Pop
//当输入右括号 ) 时，运算符栈 OPIR 顶部操作符除了 # 和 ( 优先级都比右括号 ) 高，所以先会算括号内的，直到整个括号算完
//当表达式都为同一优先级的运算符(+ 或 -)时，遇到输入字符为 # 号后开始取运算数栈 OPND 顶部的两个操作数运算，此时输入字符 c 还是 # 号，所以一直循环直到 运算符栈 OPIR 栈顶元素和当前读入的字符均为 #
OperandType EvaluateExpression() {

    //算术表达式求值的算符优先算法
    //设 OPIR 和 OPND 分别为运算符栈 和 运算数栈，OP 为运算符集合

    SqStack OPTR;
    SqStack OPND;

    InitStack(OPTR);
    Push(OPTR, '#');
    InitStack(OPND);

    printf("请输入运算表达式中的字符: ");
//    char c = getchar();
    char c;
    scanf(" %c",&c) ;

    while (c != '#' || GetTop(OPTR) != '#') {        //当满足条件 (c == '#' && GetTop(OPTR) == '#') 时退出循环

//        if (!In(c, OP)) {                          //不是运算符
        if (!In(c)) {
            Push(OPND, c);                       //则进入 运算数栈 OPND
            printf("请输入运算表达式中的字符: ");
//          c = getchar();
            scanf(" %c",&c) ;                       //%c 前面加空格防止输入空白字符
        } else {                                    //是运算符
            switch (Precede(GetTop(OPTR), c)) {     //判断 运算符栈 OPTR 顶部元素 和运算符 c 的优先级
                case '<':                           //运算符栈 OPTR 顶部元素优先权低
                    Push(OPTR, c);
                    printf("请输入运算表达式中的字符: ");
//                  c = getchar();
                    scanf(" %c",&c) ;
                    break;

                case '=':                           //脱括号并接收下一字符 (只有括号的优先级是相等的)
                    SElemType x;
                    Pop(OPTR, x);
                    printf("请输入运算表达式中的字符: ");
//                  c = getchar();
                    scanf(" %c",&c) ;
                    break;

                case '>':                           //运算符栈 OPTR 顶部元素优先级高，退栈并将运算结果入栈
                    SElemType theta;
                    Pop(OPTR, theta);

                    SElemType b;
                    Pop(OPND, b);

                    SElemType a;
                    Pop(OPND, a);

                    Push(OPND, Operate(a, theta, b));
                    break;
            }// switch
        }
    }// while

    return GetTop(OPND);
}//EvaluateExpression
#endif